﻿namespace MicroCloud.Utils
{
    /// <summary>
    /// <see cref="IDistributedCache"/> 扩展方法
    /// </summary>
    public static class DistributedCacheExtensions
    {
        #region "IDistributedCache 扩展方法"
        #region "将对象存入缓存中"
        /// <summary>
        /// 将对象存入缓存中
        /// </summary>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="value">需要缓存的对象</param>
        /// <param name="options">缓存配置</param>
        public static void Set(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions options = null)
        {
            Check.NotNullOrEmpty(key, nameof(key));
            Check.NotNull(value, nameof(value));

            string json = value.ToJsonString();
            if (options == null)
            {
                cache.SetString(key, json);
            }
            else
            {
                cache.SetString(key, json, options);
            }
        }
        #endregion
        #region "异步将对象存入缓存中"
        /// <summary>
        /// 异步将对象存入缓存中
        /// </summary>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="value">需要缓存的对象</param>
        /// <param name="options">缓存配置</param>
        /// <returns></returns>
        public static async Task SetAsync(this IDistributedCache cache, string key, object value, DistributedCacheEntryOptions options = null)
        {
            Check.NotNullOrEmpty(key, nameof(key));
            Check.NotNull(value, nameof(value));

            string json = value.ToJsonString();
            if (options == null)
            {
                await cache.SetStringAsync(key, json);
            }
            else
            {
                await cache.SetStringAsync(key, json, options);
            }
        }
        #endregion
        #region "将对象存入缓存中，使用指定时长。"
        /// <summary>
        /// 将对象存入缓存中，使用指定时长。
        /// </summary>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="value">需要缓存的对象</param>
        /// <param name="cacheSeconds">缓存秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        public static void Set(this IDistributedCache cache, string key, object value, int cacheSeconds, bool absolute = true)
        {
            Check.NotNullOrEmpty(key, nameof(key));
            Check.NotNull(value, nameof(value));

            var options = InitOptions(cacheSeconds, absolute);
            cache.Set(key, value, options);
        }
        #endregion
        #region "异步将对象存入缓存中，使用指定时长。"
        /// <summary>
        /// 异步将对象存入缓存中，使用指定时长。
        /// </summary>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="value">需要缓存的对象</param>
        /// <param name="cacheSeconds">缓存秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <returns></returns>
        public static Task SetAsync(this IDistributedCache cache, string key, object value, int cacheSeconds, bool absolute = true)
        {
            Check.NotNullOrEmpty(key, nameof(key));
            Check.NotNull(value, nameof(value));

            var options = InitOptions(cacheSeconds, absolute);
            return cache.SetAsync(key, value, options);
        }
        #endregion

        #region "获取指定键的缓存项"
        /// <summary>
        /// 获取指定键的缓存项
        /// </summary>
        /// <typeparam name="TResult">缓存对象类型</typeparam>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <returns>缓存对象</returns>
        public static TResult Get<TResult>(this IDistributedCache cache, string key)
        {
            string json = cache.GetString(key);
            if (json == null)
            {
                return default;
            }
            return json.FromJsonString<TResult>();
        }
        #endregion
        #region "异步获取指定键的缓存项"
        /// <summary>
        /// 异步获取指定键的缓存项
        /// </summary>
        /// <typeparam name="TResult">缓存对象类型</typeparam>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <returns>缓存对象</returns>
        public static async Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key)
        {
            string json = await cache.GetStringAsync(key);
            if (json == null)
            {
                return default;
            }
            return json.FromJsonString<TResult>();
        }
        #endregion
        #region "获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。"
        /// <summary>
        /// 获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。
        /// </summary>
        /// <typeparam name="TResult">缓存对象类型</typeparam>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="getFunc">获取数据委托</param>
        /// <param name="options">缓存配置</param>
        /// <returns>缓存对象</returns>
        public static TResult Get<TResult>(this IDistributedCache cache, string key, Func<TResult> getFunc, DistributedCacheEntryOptions options = null)
        {
            TResult result = cache.Get<TResult>(key);
            if (!Equals(result, default(TResult)))
            {
                return result;
            }
            result = getFunc();
            if (Equals(result, default(TResult)))
            {
                return default;
            }
            cache.Set(key, result, options);
            return result;
        }
        #endregion
        #region "异步获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。"
        /// <summary>
        /// 异步获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。
        /// </summary>
        /// <typeparam name="TResult">缓存对象类型</typeparam>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="getAsyncFunc">获取数据委托</param>
        /// <param name="options">缓存配置</param>
        /// <returns>缓存对象</returns>
        public static async Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key, Func<Task<TResult>> getAsyncFunc, DistributedCacheEntryOptions options = null)
        {
            TResult result = await cache.GetAsync<TResult>(key);
            if (!Equals(result, default(TResult)))
            {
                return result;
            }
            result = await getAsyncFunc();
            if (Equals(result, default(TResult)))
            {
                return default;
            }
            await cache.SetAsync(key, result, options);
            return result;
        }
        #endregion
        #region "获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。"
        /// <summary>
        /// 获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。
        /// </summary>
        /// <typeparam name="TResult">缓存对象类型</typeparam>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="getFunc"></param>
        /// <param name="cacheSeconds">缓存秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <returns>缓存对象</returns>
        public static TResult Get<TResult>(this IDistributedCache cache, string key, Func<TResult> getFunc, int cacheSeconds, bool absolute = true)
        {
            var options = InitOptions(cacheSeconds, absolute);
            return cache.Get(key, getFunc, options);
        }
        #endregion
        #region "异步获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。"
        /// <summary>
        /// 异步获取指定键的缓存项，不存在则从指定委托获取，并回存到缓存中再返回。
        /// </summary>
        /// <typeparam name="TResult">缓存对象类型</typeparam>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="key">缓存键</param>
        /// <param name="getAsyncFunc">获取数据委托</param>
        /// <param name="cacheSeconds">缓存秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <returns>缓存对象</returns>
        public static Task<TResult> GetAsync<TResult>(this IDistributedCache cache, string key, Func<Task<TResult>> getAsyncFunc, int cacheSeconds, bool absolute = true)
        {
            var options = InitOptions(cacheSeconds, absolute);
            return cache.GetAsync(key, getAsyncFunc, options);
        }
        #endregion

        #region "移除指定键的缓存项"
        /// <summary>
        /// 移除指定键的缓存项
        /// </summary>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="keys">缓存键</param>
        public static void Remove(this IDistributedCache cache, params string[] keys)
        {
            Check.NotNull(keys, nameof(keys));
            foreach (string key in keys)
            {
                cache.Remove(key);
            }
        }
        #endregion
        #region "移除指定键的缓存项"
        /// <summary>
        /// 移除指定键的缓存项
        /// </summary>
        /// <param name="cache">表示序列化值的分布式缓存</param>
        /// <param name="keys">缓存键</param>
        /// <returns></returns>
        public static async Task RemoveAsync(this IDistributedCache cache, params string[] keys)
        {
            Check.NotNull(keys, nameof(keys));
            foreach (string key in keys)
            {
                await cache.RemoveAsync(key);
            }
        }
        #endregion

        #endregion

        #region "私有方法"
        #region "初始化缓存配置"
        /// <summary>
        /// 初始化缓存配置
        /// </summary>
        /// <param name="cacheSeconds">缓存秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <returns>缓存配置</returns>
        private static DistributedCacheEntryOptions InitOptions(int cacheSeconds, bool absolute)
        {
            Check.GreaterThan(cacheSeconds, nameof(cacheSeconds), 0);

            DistributedCacheEntryOptions options = new();
            if (absolute)
            {
                options.SetAbsoluteExpiration(TimeSpan.FromSeconds(cacheSeconds));
            }
            else
            {
                options.SetSlidingExpiration(TimeSpan.FromSeconds(cacheSeconds));
            }
            return options;
        }
        #endregion

        #endregion


    }
}
